from fastapi import APIRouter, Depends, HTTPException, status, Response
from fastapi.responses import JSONResponse
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
from typing import Optional
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta, timezone
from config import db, apis
from method import Tasks
from ws import order
import h5py
import json
import shutil

# 密钥和算法
SECRET_KEY = apis.secret_key
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 7 * 24 * 60

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# OAuth2PasswordBearer 实例
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

router = APIRouter(default_response_class=JSONResponse)

# Pydantic 模型
class User(BaseModel):
    username: str

class UserInDB(User):
    password: str

class Token(BaseModel):
    access_token: str
    token_type: str

def get_user_data(username: str):
    try:
        # 读取数据文件（'r' 表示只读）
        with h5py.File(db.user_data, 'r') as f:
            # 读取嵌套组中的数据集
            data_list = f[username][()]
            # 把JSON字符串反序列化回对象
            data = json.loads(data_list.decode('utf-8')) 
        return data
    except Exception as e:
        return None

# 工具函数
def verify_password(plain_password, hashed_password):
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_context.hash(password)

def get_user(username: str):
    user = get_user_data(username)
    if user:
        return UserInDB(**user)
    return None

def authenticate_user(username: str, password: str):
    user = get_user(username)
    if not user or not verify_password(password, user.password):
        return False
    return user

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    expire = datetime.now(timezone.utc) + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="用户未登录",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        user_data = get_user_data(username)
        user = get_user(username)
        if user is None:
            raise credentials_exception
        if'expires_time' in user_data:
            return user
        else:
            raise credentials_exception
    except JWTError:
        raise credentials_exception

# 注册路由
@router.post("/register", response_model=User)
def register(user: UserInDB):
    data = get_user_data(user.username)
    if data:
        raise HTTPException(status_code=400, detail="用户名已存在")
    password = get_password_hash(user.password)
    users_db = {
        "username": user.username,
        "password": password,
    }
    # 创建一个数据文件（使用 'a' 模式打开文件，避免覆盖已有内容）
    with h5py.File(db.user_data, 'a') as f:
        # 把数据转成json字符串
        json_str = json.dumps(users_db)
        f.create_dataset(
            user.username,
            data=json_str,
            dtype=h5py.string_dtype()
        )
    return user

def compact_h5_file(src_path, dst_path, exclude=[]):
    with h5py.File(src_path, 'r') as src, h5py.File(dst_path, 'w') as dst:
        def copy_func(name, obj):
            if any(name.startswith(e) for e in exclude):
                return
            if isinstance(obj, h5py.Group):
                if name not in dst:
                    dst.create_group(name)
            elif isinstance(obj, h5py.Dataset):
                # 复制数据集到同样路径下
                src.copy(name, dst, name=name)
        src.visititems(copy_func)

async def del_user(username):
    # 示例：删除 path 数据集
    compact_h5_file(db.user_data, db.user_data_copy, exclude=[username])
    # 替换原文件
    shutil.move(db.user_data_copy, db.user_data)
    return True

# 删除用户路由
@router.delete("/delete")
async def delete_user(user: User = Depends(get_current_user)):
    try:
        data = get_user_data(user.username)
        if data:
            await del_user(user.username)
            return {"msg": "用户已删除"}
        raise HTTPException(status_code=404, detail="用户未找到")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
    
# 登录路由
@router.post("/login", response_model=Token)
async def login(form_data: UserInDB, response: Response):
    user = authenticate_user(form_data.username, form_data.password)
    if not user:
        raise HTTPException(status_code=400, detail="用户名或密码错误")
    access_token = create_access_token(
        data={"sub": user.username},
        expires_delta=timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    )
    data = get_user_data(form_data.username)
    if data:
        expires_time = (datetime.now() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)).timestamp()
        await del_user(form_data.username)
        data['expires_time'] = expires_time
        with h5py.File(db.user_data, 'a') as f:
            # 把数据转成json字符串
            json_str = json.dumps(data)
            f.create_dataset(
                form_data.username,
                data=json_str,
                dtype=h5py.string_dtype()
            )
        response.set_cookie(
            key="token",
            value=access_token,
            max_age=ACCESS_TOKEN_EXPIRE_MINUTES * 60,
            httponly=True,
            # secure=True,    ❌ 暂时不要加这个，避免浏览器拒绝
            samesite="lax",  # 注意：None 也需要 HTTPS，建议用 lax 本地测试
            path="/"
        )
    return {
        "access_token": access_token,
        "token_type": "bearer"
    }

@router.get("/logout")
async def user_logout(response: Response, user: User = Depends(get_current_user)):
    try:
        data = get_user_data(user.username)
        if data:
            data.pop('expires_time')
            await del_user(user.username)
            with h5py.File(db.user_data, 'a') as f:
                # 把数据转成json字符串
                json_str = json.dumps(data)
                f.create_dataset(
                    user.username,
                    data=json_str,
                    dtype=h5py.string_dtype()
                )
            response.delete_cookie(
                key="token",
                path="/",        # 必须和设置 cookie 时保持一致
                samesite="lax"   # 建议也保持一致，避免浏览器不认
            )
        return True
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

class tacticsData(BaseModel):
    symbol: Optional[str] = ''
    buyCooldown: Optional[int] = None
    amplitude24H: Optional[float] = None
    stopLoss: Optional[float] = None
    stopProfit: Optional[float] = None
    releaseCoefficient: Optional[float] = None
    slopeThreshold: Optional[float] = None
    leverage: Optional[int] = None

@router.post("/tactics")
async def user_logout(form_data: list[tacticsData], user: User = Depends(get_current_user)):
    data = get_user_data(user.username)
    if data:
        data['tactics'] = [item.model_dump() for item in form_data]
        await del_user(user.username)
        with h5py.File(db.user_data, 'a') as f:
            # 把数据转成json字符串
            json_str = json.dumps(data)
            f.create_dataset(
                user.username,
                data=json_str,
                dtype=h5py.string_dtype()
            )
        await Tasks.task_manager.stop_task('listener_def')
        await Tasks.task_manager.start_task('listener_def', order.listener_def)
    return True

@router.get("/info")
def read_users_me(user: User = Depends(get_current_user)):
    try:
        data = get_user_data(user.username)
        if 'password' in data:

            data.pop('password')
        if 'binance_secret' in data:
            data.pop('binance_secret')
        return data
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
